from abc import ABC, abstractmethod
from queue import Queue

class NodeBase(ABC):
    def __init__(self, inputs, outputs):
        super().__init__()

        self.id = None
        self.options = {}       #用户输入参数
        self.inputs = inputs    #输入列表及初始值
        self.outputs = outputs  #输出列表及初始值
        self.next: list[NodeBase] = []  #后向邻接节点列表
        self.front: list[NodeBase] = [] #前向邻接节点列表
        # 输出的流向，与输出一一对应，存储着输出流向的节点j及其第k个输入
        self.out_flow: list[list[tuple]] = outputs.copy()    
        self.executed = False       #是否执行过

    @abstractmethod
    def func(self):
        '''对inputs进行数据操作，结果存入outputs'''
        raise NotImplementedError
    
    def broadcast_outputs(self):
        '''
        将自己的运行结果，传递给后面的邻接节点
        第i个输出，对应着j节点的第k个输入
        支持多输出
        '''
        for i, out in enumerate(self.outputs):
            if(self.out_flow[i]):
                for flow in self.out_flow[i]:
                    j = flow[0] #节点j
                    k = flow[1] #第k个输入
                    j.update_input(k, out)

    def update_input(self, k, input):
        '''
        收到参数后，直接接受数据（默认），或者进行一些预处理，例如深拷贝。
        多输入的情况下，默认会按照执行顺序，覆盖掉先前的input
        要实现多输入，需要重载函数，使用例如list.append等的方法
        '''
        self.inputs[k] = input


    def add_front(self, nd):
        self.front.append(nd)
    
    def add_next(self, nd):
        self.next.append(nd)

    def add_outflow(self, i, j, k):
        #第i个输出，流向节点j的第k个输入
        if(not self.out_flow[i]):
            self.out_flow[i] = []
            
        self.out_flow[i].append((j, k))


class DataFlowExecutor:
    def exec_BFS(self, nodes: list[NodeBase]):
        q = Queue[NodeBase]()   #生成队列
        for nd in nodes:
            if not nd.front and not nd.executed:
                #对于每一个未执行的根节点
                q.put(nd)
                while not q.empty():
                    node = q.get()    #取出节点
                    
                    #有前节点未执行，退出
                    if self._not_executed(node.front): 
                        continue

                    #执行本节点，结果存入node.outputs
                    if not node.executed:
                        node.func()
                        node.broadcast_outputs()   #向后传播结果，会调用update_input
                        node.executed = True

                    for n_nd in node.next:
                        q.put(n_nd)

    def _not_executed(self, nodes:list[NodeBase]):
        for nd in nodes:
            if not nd.executed : 
                return True
        
        return False